useMemo
🧑🏻💻 useMemo
useMemo
는 리렌더링 사이의 계산 결과를 캐시 (memoization)할 수 있는 React 훅이다.
✅ useMemo 문법
const cachedValue = useMemo(calculateValue, dependencies)
calculateValue
는 캐시하려는 값을 계산하는 함수이다. React는 초기 렌더링 중에 인자없이 함수를 호출하고 그 결과를 반환하고 캐시한다. 이후 의존성이 변경되면calculateValue
를 다시 호출하여 새로운 값을 캐시한다.dependencies
는calculateValue
코드 내에서 참조되는 모든 반응형 값들의 목록이다.
🧑🏻💻 알고 가기
✅ useMemo를 왜 사용할까?
useMemo
는 계산이 많거나 무거운 함수의 반환 ‘값’을 기억하도록 도와준다. 이를 통해 동일한 계산을 반복하지 않고, 필요한 캐시된 값을 사용하여 불필요한 연산을 줄일 수 있어서 성능을 향상 시킬 수 있다. 이런 종류의 캐시를memorization
이라고 한다.
✅ useMemo
는 언제 사용해야 하는가?
- 계산이 눈에 띄게 느리면서 의존성이 거의 변하지 않는 경우에 사용하자.
useMemo
는 성능 최적화를 위해서만 사용해야한다. 그렇지 않다면 State Hooks 또는 Ref Hooks가 더 적절할 수 있다.- 인터렉션이 많이 일어나지 않는 앱의 경우 일반적으로 필요하지 않다.
✅ useMemo
와 React.memo
의 차이점은?
- useMemo는 ‘값’을 메모이제이션하여 리렌더링을 최적화하기 위해 사용된다.
- React.memo는 ‘함수 컴포넌트’를 메모이제이션하여 렌더링을 최적화하기 위해 사용된다.
✅ 주의 사항
- 루프 안에서 useMemo를 호출할 수 없다.
- 어떤
useMemo
가 다른useMemo
의 의존성으로 들어간다면, 두 로직을 통합하여 사용하는 것이 좋다.
🧑🏻💻 활용 및 생각할 거리
✅ React가 캐시된 값을 폐기하는 경우는 언제일까?
- React는 개발 중에 컴포넌트 파일을 수정하면 캐시된 값을 폐기한다.
- 개발 환경 및 상용 환경 모두에서, 초기 마운트 중에 컴포넌트가 일시 중단(suspend)되면 React는 캐시를 폐기한다.
- React는 이외 특별한 이유가 있지 않는 한 캐시된 값을 유지하려고 한다.
✅ 비용이 많이 드는 계산인지 아닌지 판단하는 방법
코드에 소요된 시간을 측정한다.
console.time('filter array');
const visibleTodos = filterTodos(todos, tab);
console.timeEnd('filter array');→ 소요된 시간이 길 때 (예: 1ms 이상) 해당 계산을 메모해 두는 것이 좋다.
useMemo
로 감싼 후 다시 소요 시간을 측정해 본다.console.time('filter array');
const visibleTodos = useMemo(() => {
return filterTodos(todos, tab);
}, [todos, tab]);
console.timeEnd('filter array');→ 이때
useMemo
는 첫 번째 렌더링을 빠르게 만들지는 않는다. 업데이트 시 최적화에만 도움이 된다.
⚠️ 참고 사항
- 개발 중에 성능을 측정하는 것은 반드시 정확한 결과를 제공하지는 않는다. → 가장 정확한 시간 측정을 위해서는 상용 앱을 빌드하고 사용자가 사용하는 것과 동일한 기기에서 테스트하자.
- 개발자의 컴퓨터가 유저의 컴퓨터보다 빠를 수 있기 때문에 인위적으로 속도를 늦춰 성능을 테스트하자. → Chrome은 이를 위한 CPU 쓰로틀링 옵션을 제공한다. → 또한 React 개발자 도구 profiler를 사용하여 메모화가 필요한 컴포넌트를 판단할 수 있다.
✅ 함수를 캐시하고 싶다면?
// useMemo
export default function Page({ productId, referrer }) {
const handleSubmit = useMemo(() => {
return (orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
};
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
// useCallback
export default function Page({ productId, referrer }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
useMemo
를 사용하면 중첩 함수를 추가로 작성하게 된다. 이럴 필요 없이 함수를 캐시할 때는useMemo
대신useCallback
으로 감싸자. 그 외 다른 이점은useMemo
와 동일하다.